const fs = require("fs");
const path = require("path");
const xlsx = require("node-xlsx");
const axios = require("axios");
const CryptoJS = require("crypto-js");

const srcFlag = false;  // src文件夹是否处理 处理产品根据翻译对象{aaa: 全部//total}情况
const excelFlag = false;  // 是否根据excel翻译
const youdaoFlag = false;  // 是否开启有道翻译
const appKey = ""; // 有道翻译appId
const key = ""; // 有道翻译的key

const reg1 = /[\u4E00-\u9FA5]/;
const deletionWords = [];  // 缺失信息
const allTranslateWords = []; // 所有翻译文本
let excelMap = {}; // 翻译的对象
init();

async function init() {
  handleDeleteDist("./dist");
  excelFlag && handleTransExcel("./src/trans.xlsx");  // 获取翻译文件
  await compareLang("../src/lang", { "zh.js": "en.js", "zhCode.js": "enCode.js" }, { zh: "zh", en: "en" });
}

/**
 * 配置指定的映射关系
 * @param url 需要比较的中英文的地方
 * @param special: {中文文件名: 英文文件名}  例如 {"zhCode.js": "enCode.js"}
 * @param folder: { zh: '中文文件夹名称', en: '英文文件夹名称' } 没有则默认中英文同文件
 */
async function compareLang(url, special = {}, folder = {}) {
  const lastURL = url.split("/").pop();
  const specialArr = Object.keys(special);
  await fileDisplay(url, async (filename, filePath, filedir) => {
    // 验证是否是中文文件夹 filename === "bulletin.js" &&
    const lastFileURL = filePath.split("\\").pop();
    if (lastFileURL === folder.zh || (lastFileURL === lastURL && specialArr.indexOf(filename) !== -1)) {
      const zhName = filename;
      const enName = special[filename] || filename;
      const zhURL = filePath + `\\` + zhName;
      const enURL = filePath.replace(folder.zh, folder.en) + `\\` + enName;

      const deletion = []; //缺失字段
      const trans = [];
      const untranslated = []; //缺失字段
      const { langMap: zhMap } = getFileContent(zhURL);

      // 处理中英文对比结果
      try {
        fs.accessSync(enURL); // 读取文件查看是否存在
        const tmap = excelMap[enName]
        const { langMap: enMap, lines } = getFileContent(enURL, zhMap, tmap);
        excelFlag && generateTransJs({ enName, lines });
        const keys = Object.keys(zhMap);
        for (let i = 0; i < keys.length; i++) {
          const zhMapKey = keys[i];
          if (!enMap[zhMapKey]) {
            // if(excelFlag && tmap[zhMap[zhMapKey].value]) {
            //   continue
            // }
            deletion.push({
              key: zhMapKey,
              zh: zhMap[zhMapKey].value,
              index: zhMap[zhMapKey].index, // 缺失应该存在的index
              url: enURL
            });
          } else {
            if (reg1.test(enMap[zhMapKey].value)) {
              // if(excelFlag && tmap[zhMap[zhMapKey].value]) {
              //   continue
              // }
              let youdao = "";
              if (youdaoFlag) {
                const { translation } = await getYouDaoTrans(zhMap[zhMapKey].value);
                youdao = translation[0]
              }
              untranslated.push({
                key: zhMapKey,
                zh: zhMap[zhMapKey].value,
                index: enMap[zhMapKey].index, // 英文未翻译的index
                url: enURL,
                youdao
              });
            }
          }

          trans.push({
            zh: zhMap[zhMapKey].value,
            en: enMap[zhMapKey] ? enMap[zhMapKey].value : ""
          });
        }

        (deletion.length || untranslated.length) && deletionWords.push({     // 缺失文件
          name: enName,
          data: deletion,
          untranslated,
          url: enURL
        });

        (!srcFlag) && allTranslateWords.push({  // 全模块翻译文件
          name: enName,
          data: trans
        });

      } catch (err) {
        const { lines } = getFileContent(zhURL, zhMap, excelMap[enName]);
        excelFlag && generateTransJs({ enName, lines });
        console.error(`${enURL}, 英文翻译脚本缺失`);
      }

      // 处理src文件夹下翻译文件 (临时)
      handleSrcDir({ enName });
    }
  });

  if (deletionWords.length) {
    generateExcelFile();
  } else {
    console.error(`success -------> 生无翻译缺失`);
  }

  // 生成翻译的文件模块excel 不是按照excel翻译的情况下才会生成
  if (allTranslateWords.length && !excelFlag) {
    generateTransExcelFile();
  }
}

/**
 * 临时 - 处理src文件夹翻译
 * @param enName
 */
function handleSrcDir({ enName }) {
  if (!srcFlag) return;
  const srcURL = path.resolve(__dirname, `src/${enName}`);
  try {
    fs.accessSync(srcURL); // 读取文件查看是否存在
    const { lines, srcMap } = getFileContent(srcURL);
    generateTransJs({ enName, lines });
    // 按模块生成
    allTranslateWords.push({
      name: enName,
      data: srcMap
    });
  } catch (err) {
    console.error(`${srcURL}, 英文翻译脚本缺失`);
  }
}

/**
 * 重新翻译后的JS文件
 * @param enName 英文名称
 * @param lines 数组每一行文件
 */
function generateTransJs({ enName, lines }) {
  if (!fs.existsSync("./dist")) {
    fs.mkdirSync(path.join(__dirname, "./dist"));
  }
  if (!fs.existsSync("./dist/translated")) {
    fs.mkdirSync(path.join(__dirname, "./dist/translated"));
  }
  // 生成翻译的js文件
  const url = path.resolve(__dirname, `dist/translated/${enName}`);
  fs.writeFile(url, lines.join("\n"), { "flag": "w" }, function(err) {
    if (err) return console.error("Error:(writeFile)", err);
    console.log(`success -------> ${url}`);
  });
}

/**
 * 获取文件内信息
 * @param url
 * @param zhMap
 * @param transMap
 * @returns {{}}
 */
function getFileContent(url, zhMap = null, transMap = null) {
  const langMap = {};
  const srcMap = []; // 单独处理src文件
  const newLines = [];   // 单独处理src文件
  let data = fs.readFileSync(url);
  const lines = data.toString().split(/\r?\n/);

  lines.forEach((line, index) => {
    const firstIndex = line.indexOf(":");
    let srcNewLine = line;
    if (firstIndex !== -1) {
      let line1 = line.substring(0, firstIndex).replace(/\t/g, "").trim();
      let line2 = line.substring(firstIndex + 1, line.length).replace(/\t/g, "").trim();

      line1 = startWithMarks(line1);
      line2 = startWithMarks(line2.endsWith(",") ? line2.substring(0, line2.length - 1) : line2);

      line2 !== "{" && (langMap[line1] = {
        value: line2,
        index: index + 1
      });

      // 临时 - 处理src文件夹翻译
      if (srcFlag) {
        const line2Arr = line2.split("//");
        let line2Arr1 = line2Arr[0];
        let line2Arr2 = line2Arr[1];
        line2Arr1 = startWithMarks(line2Arr1.endsWith(",") ? line2Arr1.substring(0, line2Arr1.length - 1) : line2Arr1);
        if (line2Arr2) {
          srcNewLine = srcNewLine.replace(line2Arr1, line2Arr2);
          const index = srcNewLine.indexOf("//");
          index !== -1 && (srcNewLine = srcNewLine.substring(0, index));
          // srcNewLine = srcNewLine + `//${line2Arr1}`
        }
        srcMap.push({ zh: line2Arr1, en: line2Arr2, youdao });
      } else if (excelFlag && transMap) {
        const zh = zhMap[line1] ? zhMap[line1].value : undefined;
        if (zh && transMap[zh]) {
          srcMap.push({ zh: zh, en: transMap[zh] });
          line2 !== "{" && (srcNewLine = srcNewLine.replace(line2, transMap[zh]));
        }
      }
    }
    newLines.push(srcNewLine);
  });
  return { langMap, lines: newLines, srcMap };
}

/**
 * 处理开头和结尾是`'"的数据
 * @param str
 * @returns {*|string}
 */
function startWithMarks(str) {
  const reg = /(^['"`])(.*?)(\1)$/g;
  const reg1 = /(^['"`])(.*?)(\1)$/;
  if (reg.test(str)) {
    return str.match(reg1)[2] || "";
  }
  return str;
}

/**
 * 按照模块生成翻译文件
 */
function generateTransExcelFile() {
  if (!fs.existsSync("./dist")) {
    fs.mkdirSync(path.join(__dirname, "./dist"));
  }
  let length = 0;
  const buffer = xlsx.build(allTranslateWords.map(item => {
    const dataLength = item.data.length;
    length = dataLength > length ? dataLength : length;
    return {
      name: item.name, data: [["中文", "原有英文", "重新翻译英文", "名词解释"], ...item.data.map(wordInfo => {
        return [wordInfo.zh, wordInfo.en];
      })]
    };
  }), {
    sheetOptions: {
      "!cols": [{ wpx: 200 }, { wpx: 300 }, { wpx: 300 }, { wpx: 300 }],
      "!rows": new Array(length + 5).fill({ hpx: 20 })
    }
  });
  fs.writeFile(path.join(__dirname, "./dist/全模块翻译.xlsx"), buffer, { "flag": "w" }, function(error) {
    if (error) return console.error("Error:(writeFileExcel)", error);
    console.log(`success -------> 生成全模块翻译文件.xlsx`);
  });
}


/**
 * 生成未翻译的excel
 */
function generateExcelFile() {
  if (!fs.existsSync("./dist")) {
    fs.mkdirSync(path.join(__dirname, "./dist"));
  }
  let length = 0;
  const buffer = xlsx.build(deletionWords.map(item => {
    const dataLength = item.data.length + item.untranslated.length;
    length = dataLength > length ? dataLength : length;
    return {
      name: item.name, data: [["缺失字段", item.url], ...item.data.map(wordInfo => {
        return [wordInfo.key, wordInfo.zh, wordInfo.index];
      }), [], [], ["未翻译", "", "", "机翻"], ...item.untranslated.map(wordInfo => {
        return [wordInfo.key, wordInfo.zh, wordInfo.index, wordInfo.youdao];
      })]
    };
  }), {
    sheetOptions: {
      "!cols": [{ wpx: 200 }, { wpx: 300 }, {wpx: 100}, {wpx: 500}],
      "!rows": new Array(length + 5).fill({ hpx: 20 })
    }
  });
  fs.writeFile(path.join(__dirname, "./dist/英文缺失.xlsx"), buffer, { "flag": "w" }, function(error) {
    if (error) return console.error("Error:(writeFileExcel)", error);
    console.log(`success -------> 生成缺失文件.xlsx`);
    excelFlag && console.log(`success -------> 本次对比以中英文js为主，不牵扯excel文件`)
    (excelFlag || srcFlag) && console.log(`success -------> 请替换的翻译文件 ./dist/translated`)
  });
}

/**
 * 同步递归获取文件夹下所有的数据
 * @param url
 * @param cb
 */
async function fileDisplay(url, cb) {
  try {
    const filePath = path.resolve(url);
    //根据文件路径读取文件，返回文件列表
    const files = fs.readdirSync(filePath);
    for (let i = 0; i<files.length; i++) {
      const filename = files[i]
      //获取当前文件的绝对路径
      const filedir = path.join(filePath, filename);
      // fs.stat(path)执行后，会将stats类的实例返回给其回调函数。
      const stats = fs.statSync(filedir);
      // 是否是文件
      const isFile = stats.isFile();
      // 是否是文件夹
      const isDir = stats.isDirectory();
      if (isFile) {
        cb && await cb(filename, filePath, filedir);
      }
      // 如果是文件夹
      if (isDir) await fileDisplay(filedir, cb);
    }
  } catch (e) {
    throw e;
  }

}


/**
 *  处理翻译excel文件
 */
function handleTransExcel(url) {
  let list = xlsx.parse(path.resolve(url));
  excelMap = list.reduce((prev, item) => {
    prev[item.name] = item.data.reduce((res, trans) => {
      res[trans[0]] = trans[2] || trans[1];
      return res;
    }, {});
    return prev;
  }, {});
}


/**
 * 移除文件夹
 * @param rootPath
 */
function handleDeleteDist(rootPath) {
  if (fs.existsSync(rootPath)) {  //先删除文件   rootPath文件夹路径
    let files = fs.readdirSync(rootPath);
    if (files.length) {
      files.forEach(function(file, index) {
        const curPath = rootPath + "/" + file;
        if (fs.statSync(curPath).isDirectory()) { // recurse
          const dir = fs.readdirSync(curPath);
          if (dir.length) {
            handleDeleteDist(curPath);
          } else {
            fs.rmdirSync(curPath);
          }
        } else { // delete file
          fs.unlinkSync(curPath);
        }
      });
    } else {
      fs.rmdirSync(rootPath);
    }
  }
}

/**
 * 有道翻译请求
 */
function getYouDaoTrans(query) {
  const from = "zh-CHS";
  const to = "en";
  const salt = (new Date).getTime();
  const curtime = Math.round(new Date().getTime() / 1000);
  const str1 = appKey + truncate(query) + salt + curtime + key;
  const sign = CryptoJS.SHA256(str1).toString(CryptoJS.enc.Hex);

  return new Promise((resolve, reject) => {
    axios({
      url: "https://openapi.youdao.com/api",
      method: "get",
      params: {
        q: query,
        appKey: appKey,
        salt: salt,
        from: from,
        to: to,
        sign: sign,
        signType: "v3",
        curtime: curtime
      }
    }).then(res => {
      resolve(res.data);
    }).catch(error => {
      reject(error);
    });
  });
}

/**
 *
 * @param q
 * @returns {string|*}
 */
function truncate(q) {
  const len = q.length;
  if (len <= 20) return q;
  return q.substring(0, 10) + len + q.substring(len - 10, len);
}
